/*
 * File:   driver_i2c.c
 * Author: Ich
 *
 * Created on 23.02.2014, 16:03
 *
 */

#include <plib.h>
#include "driver_i2c.h"
#include "api_clk.h"
/*******************************************************************************
 *  private preprocessing definitions
 */




 /*******************************************************************************
 *  private variables
 */

/*******************************************************************************
 *  private functions
 */

 /* This is a blocking routine that waits for the bus to be idle and the Start
 *  (or Restart) signal to complete.
 *
 * param(in)    module      which module I2C1...I2C5
 * param(in)    restart     true = send "restart" condition, false = send "start" condition
 *
 * return       successful  true = ok, false = error
 */
static BOOL drvI2C_startTransfer(I2C_MODULE module, BOOL restart)
{
    BOOL successful = TRUE;
    I2C_STATUS  status;

    // Send the Start (or Restart) signal
    if(restart)
    {
        // Wait for the bus to be idle, then start the transfer
        //while( !I2CBusIsIdle(module) );
        successful = (I2CRepeatStart(module) == I2C_SUCCESS);
    }
    else
    {
        // Wait for the bus to be idle, then start the transfer
        while( !I2CBusIsIdle(module) );
        successful = (I2CStart(module) == I2C_SUCCESS);
    }

    // Wait for the signal to complete
    if(successful)
    {
        do
        {
            status = I2CGetStatus(module);

        } while ( !(status & I2C_START) );
    }

    return successful;
}


/* This is a blocking routine that waits for the Stop signal to complete.
 *
 * param(in)    module      which module I2C1...I2C5
 *
 */
static void drvI2C_stopTransfer(I2C_MODULE module)
{
    I2C_STATUS  status;

    // Send the Stop signal
    I2CStop(module);

    // Wait for the signal to complete
    do
    {
        status = I2CGetStatus(module);

    } while ( !(status & I2C_STOP) );
}


/* This is a blocking routine that waits for the transmission to complete.
 *
 * param(in)    module      which module I2C1...I2C5
 * param(in)    data        byte to transmit
 *
 * return       successful  true = ok, false = error
 *
 */
static BOOL drvI2C_Transmit1Byte(I2C_MODULE module, UINT8 data)
{
    // Wait for the transmitter to be ready
    while(!I2CTransmitterIsReady(module));

    // Transmit the byte
    BOOL successful = (I2CSendByte(module, data) == I2C_SUCCESS);

    // Wait for the transmission to finish
    while(successful && !I2CTransmissionHasCompleted(module));

    if(successful)
    {
        // Verify that the byte was acknowledged
        successful = I2CByteWasAcknowledged(module);
    }

    return successful;
}

 /*******************************************************************************
 *  public variables (defined by external)
 */


/*******************************************************************************
 *  public functions
 */

BOOL drvI2C_init(I2C_MODULE module, UINT32 clkSpeed)
{
    UINT32 actualClock = I2CSetFrequency(module,
                                         apiClk_getPeripheralClock(),
                                         clkSpeed);

    BOOL successful = ( abs(actualClock-clkSpeed) <= clkSpeed/10 );
    
    // Enable the I2C bus
    I2CEnable(module, TRUE);

    return successful;
}


BOOL drvI2C_write(
        I2C_MODULE module, I2C_7_BIT_ADDRESS slaveAddr,
        UINT8 *pSlaveRegAddr, UINT8 slaveRegLen,
        UINT8 *pData, UINT8 dataLen)
{
    BOOL success = TRUE;
    
    // Start the transfer to write data 
    if( !drvI2C_startTransfer(module, FALSE) )
    {
        while(1);
    }

    // Transmit slave address
    I2C_FORMAT_7_BIT_ADDRESS(slaveAddr, slaveAddr.address, I2C_WRITE);
    success = drvI2C_Transmit1Byte(module, slaveAddr.byte);

    // Transmit register address
    UINT8 index = 0;
    while( success && (index < slaveRegLen) )
    {
        // Transmit a byte
        success = drvI2C_Transmit1Byte(module, pSlaveRegAddr[slaveRegLen]);
        if(success)
        {
            // Advance to the next byte
            index++;
        }
    }

    // Transmit data
    index = 0;
    while( success && (index < dataLen) )
    {
        // Transmit a byte
        success = drvI2C_Transmit1Byte(module, pData[index]);
        if(success)
        {
            // Advance to the next byte
            index++;
        }
    }

    drvI2C_stopTransfer(module);
    return success;
}


BOOL drvI2C_writeByte(
        I2C_MODULE module, I2C_7_BIT_ADDRESS slaveAddr,
        UINT8 slaveRegAddr, UINT8 data)
{
    BOOL success = TRUE;

    // Start the transfer to write data
    if( !drvI2C_startTransfer(module, FALSE) )
    {
        while(1);
    }

    // Transmit slave address
    I2C_FORMAT_7_BIT_ADDRESS(slaveAddr, slaveAddr.address, I2C_WRITE);
    success = drvI2C_Transmit1Byte(module, slaveAddr.byte);

    // Transmit register address
    if( success )
    {
        // Transmit a byte
        success = drvI2C_Transmit1Byte(module, slaveRegAddr);
    }

    // Transmit data
    if( success )
    {
        // Transmit a byte
        success = drvI2C_Transmit1Byte(module, data);
    }

    drvI2C_stopTransfer(module);
    return success;
}

BOOL drvI2C_read(
        I2C_MODULE module, I2C_7_BIT_ADDRESS slaveAddr,
        UINT8 *pSlaveRegAddr, UINT8 slaveRegLen,
        UINT8 *pData, UINT8 dataLen)
{
    BOOL success = TRUE;
    // Start the transfer to read the EEPROM.
    if( !drvI2C_startTransfer(module,FALSE) )
    {
        while(1);
    }


    // Transmit slave address
    I2C_FORMAT_7_BIT_ADDRESS(slaveAddr, slaveAddr.address, I2C_WRITE);
    success = drvI2C_Transmit1Byte(module, slaveAddr.byte);

    // Transmit device registers
    UINT8 index = 0;
    while( success && (index < slaveRegLen) )
    {
        // Transmit a byte
        success = drvI2C_Transmit1Byte(module, pSlaveRegAddr[index]);
        if(success)
        {
            // Advance to the next byte
            index++;
        }
    }

    // Restart and send the EEPROM's internal address to switch to a read transfer
    if(success)
    {
        // Send a Repeated Started condition
        if( !drvI2C_startTransfer(module,TRUE) )
        {
            while(1);
        }

        // Transmit the address with the READ bit set
        I2C_FORMAT_7_BIT_ADDRESS(slaveAddr, slaveAddr.address, I2C_READ);
        success = drvI2C_Transmit1Byte(module, slaveAddr.byte);
    }

    // Read all data from the desired address
    if(success)
    {
        success = (I2CReceiverEnable(module, TRUE) == I2C_SUCCESS);
        index = 0;
        while(success && index < dataLen)
        {
            while(!I2CReceivedDataIsAvailable(module));
            pData[index++] = I2CGetByte(module);
            
            // send ACK if more data are required, else send NACK
            I2CAcknowledgeByte(module, index < dataLen);
            while(!I2CAcknowledgeHasCompleted(module));
        }
    }

    // End the transfer 
    drvI2C_stopTransfer(module);
    return success;
}

BOOL drvI2C_readByte(
        I2C_MODULE module, I2C_7_BIT_ADDRESS slaveAddr,
        UINT8 slaveRegAddr, UINT8 *pData)
{
    BOOL success = TRUE;
    // Start the transfer to read the EEPROM.
    if( !drvI2C_startTransfer(module,FALSE) )
    {
        while(1);
    }


    // Transmit slave address
    I2C_FORMAT_7_BIT_ADDRESS(slaveAddr, slaveAddr.address, I2C_WRITE);
    success = drvI2C_Transmit1Byte(module, slaveAddr.byte);

    // Transmit device register address
    if( success )
    {
        // Transmit a byte
        success = drvI2C_Transmit1Byte(module, slaveRegAddr);
    }

    // Restart and send the slave address to switch to a read transfer
    if(success)
    {
        // Send a Repeated Started condition
        if( !drvI2C_startTransfer(module,TRUE) )
        {
            while(1);
        }

        // Transmit the address with the READ bit set
        I2C_FORMAT_7_BIT_ADDRESS(slaveAddr, slaveAddr.address, I2C_READ);
        success = drvI2C_Transmit1Byte(module, slaveAddr.byte);
    }

    // Read all data from the desired address
    if(success)
    {
        success = (I2CReceiverEnable(module, TRUE) == I2C_SUCCESS);
        if(success)
        {
            while(!I2CReceivedDataIsAvailable(module));
            *pData = I2CGetByte(module);

            // send NACK to stop reading
            I2CAcknowledgeByte(module, FALSE);
            while(!I2CAcknowledgeHasCompleted(module));
        }
    }

    // End the transfer
    drvI2C_stopTransfer(module);
    return success;
}





